001    /**
002     * Created by IntelliJ IDEA.
003     * User: Wei Wang
004     * Date: Nov 27, 2002
005     * Time: 3:50:11 PM
006     */
007    
008    package EVolve.visualization;
009    
010    import EVolve.data.*;
011    import EVolve.Scene;
012    import EVolve.visualization.VizFactory.VisualizationFactory;
013    
014    import javax.swing.*;
015    import javax.imageio.*;
016    import java.awt.event.*;
017    import java.awt.image.BufferedImage;
018    import java.awt.*;
019    import java.util.*;
020    import java.io.*;
021    
022    public abstract class Visualization implements Cloneable{
023        public static final int SELECT_TIME_FRAME = 0x0001;
024        public static final int SELECT_OCCURRED_ENTITIES = 0x0010;
025        public static final int SELECT_ALL_ENTITIES = 0x0100;
026        public static final int SELECT_X_AXIS = 0x1000;
027        public static final int SELECT_Y_AXIS = 0x0100;
028        public static final int SELECT_ALL_X_AXIS = 0x0010;
029        public static final int SELECT_ALL_Y_AXIS = 0x0001;
030    
031    
032        protected String name; // name of the visualization
033        protected EVolve.Window window; // window where the visualization is displayed
034    
035        protected VisualizationDefinition definition; // definition of the visualization
036        protected ElementDefinition subjectDefinition; // definition of the subject
037    
038        protected Dimension[] dimension; // dimension of the visualization
039        protected Component panel; // panel to draw the visualization
040    
041        protected JDialog dialog; // configuration dialog
042        protected JPanel panelConfiguration; // configuration panel
043        protected JComboBox comboSubject; // subject selection
044        protected JComboBox[] comboDimension; // dimension selection
045        protected JTextField textName; // name input
046    
047        protected JPopupMenu popup; // popup menu
048        private JMenu menuSort;
049        private JMenu[] menuDimension;
050        protected JMenuItem[][] itemSort;
051        private JMenuItem itemConfigure,itemRename;
052        private JMenuItem itemSelect,itemSave;
053        private JMenuItem itemClone;
054        private ReferenceDimension[] referenceDimension;
055        private int linkageId;
056        private VisualizationFactory factory = null;
057        private static int vizIDPool = 0;
058        protected int dataSourceId;
059    
060        protected ElementDefinition[] elementDefinition; // definition of elements that can be used as subject
061        protected DataFilter[][][] dataFilter; // data filters that can be used by the dimensions
062        protected int mouseX, mouseY;
063    
064        protected boolean freezed;
065    
066        public Visualization() {
067            dimension = createDimension();
068            panel = createPanel();
069            panelConfiguration = null;
070            dialog = null;
071            linkageId = vizIDPool++;
072            addPopupTrigger(panel);
073            freezed = false;
074        }
075    
076        public String getName() {
077            return name;
078        }
079    
080        /**
081         * Sets the name of the visualization.
082         *
083         * @param  name  name of the visualization
084         */
085        public void setName(String name) {
086            this.name = name;
087        }
088    
089        /**
090         * Sets the window of the visualization.
091         *
092         * @param  window  window of the visualization
093         */
094        public void setWindow(EVolve.Window window) {
095            this.window = window;
096        }
097    
098        /**
099         * Gets the panel of the visualization.
100         *
101         * @return  panel of the visualization
102         */
103        public Component getPanel() {
104            return panel;
105        }
106    
107        /**
108         * Gets the additional configuration panel.
109         *
110         * @return  the additional configuration panel, null if not necessary
111         */
112        protected JPanel createConfigurationPanel() {
113            return null; // by default, return null, each visualization can override this
114        }
115    
116        protected void createMenu() {
117            popup = new JPopupMenu();
118    
119            itemConfigure = new JMenuItem("Configure...");
120            itemConfigure.setMnemonic(KeyEvent.VK_C);
121            itemConfigure.addActionListener(new ActionListener() {
122                public void actionPerformed(ActionEvent e) {
123                    configure();
124                }
125            });
126            popup.add(itemConfigure);
127    
128            itemRename = new JMenuItem("Rename...");
129            itemRename.setMnemonic(KeyEvent.VK_R);
130            itemRename.addActionListener(new ActionListener() {
131                public void actionPerformed(ActionEvent e) {
132                    String newName = JOptionPane.showInputDialog(Scene.getFrame(),"Enter a new name:",name);
133                    if (newName != null) {
134                        name = newName;
135                        window.setTitle(name);
136                    }
137                }
138            });
139            popup.add(itemRename);
140    
141            itemSelect = new JMenuItem("Make Selection");
142            itemSelect.setMnemonic(KeyEvent.VK_M);
143            itemSelect.addActionListener(new ActionListener() {
144                public void actionPerformed(ActionEvent e) {
145                    makeSelection();
146                }
147            });
148            popup.add(itemSelect);
149    
150            menuSort = new JMenu("Sort");
151            menuSort.setMnemonic(KeyEvent.VK_S);
152            menuSort.setEnabled(false);
153            popup.add(menuSort);
154    
155            ArrayList tempList = new ArrayList();
156            for (int i = 0; i < dimension.length; i++) {
157                if (dimension[i] instanceof ReferenceDimension) {
158                    tempList.add(new Integer(i));
159                }
160            }
161    
162            referenceDimension = new ReferenceDimension[tempList.size()];
163            menuDimension = new JMenu[tempList.size()];
164            itemSort = new JMenuItem[tempList.size()][];
165            for (int i = 0; i < tempList.size(); i++) {
166                int j = ((Integer)(tempList.get(i))).intValue();
167                referenceDimension[i] = (ReferenceDimension)(dimension[j]);
168                menuDimension[i] = new JMenu(definition.getDimensionDefinition()[j].getName());
169                menuSort.add(menuDimension[i]);
170            }
171    
172            itemClone = new JMenuItem("Clone");
173            itemClone.setMnemonic(KeyEvent.VK_C);
174            itemClone.addActionListener(new ActionListener() {
175                public void actionPerformed(ActionEvent e) {
176                    Scene.getVisualizationManager().cloneVisualization();
177                }
178            });
179            popup.add(itemClone);
180    
181            itemSave = new JMenuItem("Save...");
182            itemSave.setMnemonic(KeyEvent.VK_V);
183            itemSave.addActionListener(new ActionListener() {
184                public void actionPerformed(ActionEvent e) {
185                    save();
186                }
187            });
188            popup.add(itemSave);
189        }
190    
191        private void showPopup(MouseEvent e) {
192            mouseX = e.getX();
193            mouseY = e.getY();
194            Rectangle rect = e.getComponent().getBounds();
195            Rectangle rect2 = popup.getBounds();
196            int posX = mouseX, posY = mouseY;
197            if (mouseY+rect2.height > rect.height)
198                posY = posY - rect2.height;
199            if (mouseX+rect2.width>rect.width)
200                posX = posX - rect2.width;
201                popup.show(e.getComponent(), posX, posY);
202        }
203    
204        /**
205         * Sets the definition of the visualization, called by visualization manager after the visualization is created.
206         *
207         * @param  definition  definition of the visualization
208         */
209        public void setDefinition(VisualizationDefinition definition) {
210            panelConfiguration = createConfigurationPanel();
211    
212            this.definition = definition;
213    
214            elementDefinition = Scene.getDataManager().getElementDefinition(definition);
215    
216            dataFilter = new DataFilter[elementDefinition.length][definition.getDimensionDefinition().length][];
217            for (int i = 0; i < dataFilter.length; i++) {
218                for (int j = 0; j < dataFilter[i].length; j++) {
219                    dataFilter[i][j] = Scene.getDataManager().getDataFilter(elementDefinition[i],definition.getDimensionDefinition()[j].getProperty());
220                    for (int k = 0; k < dataFilter[i][j].length; k++) {
221                        assert (((dataFilter[i][j][k].getTargetType() != -1) && (dimension[j] instanceof ReferenceDimension)) || ((dataFilter[i][j][k].getTargetType() == -1) && (dimension[j] instanceof ValueDimension))) : "Incompatible dimension type.";
222                    }
223                }
224            }
225    
226            subjectDefinition = elementDefinition[0];
227            for (int i = 0; i < dimension.length; i++) {
228                dimension[i].setDataFilter(dataFilter[0][i][0]);
229            }
230    
231            updateConfiguration();
232    
233            createMenu();
234        }
235    
236        /**
237         * Gets the definition of the subject.
238         *
239         * @return  definition of the subject
240         */
241        public ElementDefinition getSubjectDefinition() {
242            return subjectDefinition;
243        }
244    
245        /**
246         * Configures the visualization.
247         */
248        public void configure() {
249            if (dialog == null) {
250                createDialog();
251            }
252            dialog.pack();
253            Scene.getUIManager().showDialog(dialog, dialog.getWidth(), dialog.getHeight());
254        }
255    
256        /**
257         * Save visualizations as disk file
258         */
259        public void save() {
260            Component target = panel;
261            if (target instanceof JScrollPane)
262                target = ((JScrollPane)panel).getViewport().getView();
263    
264            BufferedImage image = new BufferedImage(target.getWidth(), target.getHeight(), BufferedImage.TYPE_INT_RGB);
265            target.paint(image.getGraphics());
266            ImageWriter writer = (ImageWriter)(ImageIO.getImageWritersByFormatName("png").next());
267    
268            JFileChooser fc = new JFileChooser(Scene.getUIManager().getLastResultDir());
269            fc.setFileFilter(new javax.swing.filechooser.FileFilter() {
270                public String getDescription() {
271                    return "PNG Files";
272                }
273    
274                public boolean accept(File f) {
275                    return f.getName().substring(f.getName().lastIndexOf('.') + 1).toLowerCase().equals("png");
276                }
277            });
278    
279            if (fc.showSaveDialog(Scene.getFrame()) == JFileChooser.APPROVE_OPTION) {
280                File file = fc.getSelectedFile();
281                Scene.getUIManager().setLastResultDir(file.getPath());
282                try {
283                    writer.setOutput(ImageIO.createImageOutputStream(file));
284                    writer.write(image);
285                } catch (IOException e) {}
286            }
287        }
288    
289        /**
290         * Creates the configuration dialog.
291         */
292        protected void createDialog() {
293            dialog = new JDialog(Scene.getFrame(), "Configure", true);
294    
295            JPanel panelTitle = new JPanel(new FlowLayout());
296            dialog.getContentPane().add(panelTitle, BorderLayout.NORTH);
297    
298            panelTitle.add(new JLabel("Title: "));
299    
300            textName = new JTextField(name, 12);
301            panelTitle.add(textName);
302    
303            JPanel panelMain = new JPanel(new BorderLayout());
304            panelMain.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Choose subject & dimensions"));
305            dialog.getContentPane().add(panelMain, BorderLayout.CENTER);
306    
307            JPanel panelName = new JPanel(new GridLayout(definition.getDimensionDefinition().length + 1, 1, 5, 5));
308            panelMain.add(panelName, BorderLayout.WEST);
309    
310            panelName.add(new JLabel(" Subject:      "));
311            for (int i = 0; i < definition.getDimensionDefinition().length; i++) {
312                panelName.add(new JLabel(" " + definition.getDimensionDefinition()[i].getName() + ":      "));
313            }
314    
315            JPanel panelCombo = new JPanel(new GridLayout(definition.getDimensionDefinition().length + 1, 1, 5, 5));
316            panelMain.add(panelCombo, BorderLayout.CENTER);
317    
318            comboSubject = new JComboBox();
319            for (int i = 0; i < elementDefinition.length; i++) {
320                comboSubject.addItem(elementDefinition[i].getName());
321            }
322            comboSubject.addActionListener(new ActionListener() {
323                public void actionPerformed(ActionEvent e) {
324                    updateComboSubject();
325                }
326            });
327            panelCombo.add(comboSubject);
328    
329            comboDimension = new JComboBox[definition.getDimensionDefinition().length];
330            for (int i = 0; i < comboDimension.length; i++) {
331                comboDimension[i] = new JComboBox();
332                comboDimension[i].addActionListener(new ActionListener() {
333                    public void actionPerformed(ActionEvent e) {
334                        updateComboDimension();
335                    }
336                });
337                panelCombo.add(comboDimension[i]);
338            }
339    
340            updateComboSubject();
341    
342            if (panelConfiguration != null) {
343                panelMain.add(panelConfiguration, BorderLayout.SOUTH);
344            }
345    
346            JPanel panelButton = new JPanel(new FlowLayout());
347            dialog.getContentPane().add(panelButton, BorderLayout.SOUTH);
348    
349            JButton buttonApply = new JButton("Apply");
350            buttonApply.addActionListener(new ActionListener() {
351                public void actionPerformed(ActionEvent e) {
352                    dialogApply();
353                }
354            });
355            panelButton.add(buttonApply);
356    
357            JButton buttonCancel = new JButton("Cancel");
358            buttonCancel.addActionListener(new ActionListener() {
359                public void actionPerformed(ActionEvent e) {
360                    dialogCancel();
361                }
362            });
363            panelButton.add(buttonCancel);
364    
365            //*************
366            for (int i=0; i<comboSubject.getItemCount(); i++) {
367                if ((comboSubject.getItemAt(i)).equals(subjectDefinition.getName())) {
368                    comboSubject.setSelectedIndex(i);
369                    break;
370                }
371            }
372            for (int i=0;i<comboDimension.length;i++) {
373                for (int j =0 ; j<comboDimension[i].getItemCount();j++) {
374                    if (comboDimension[i].getItemAt(j).equals(dimension[i].getDataFilter().getName())) {
375                        comboDimension[i].setSelectedIndex(j);
376                        break;
377                    }
378                }
379            }
380        }
381    
382        /**
383         * Updates the subject combo-box.
384         */
385        protected void updateComboSubject() {
386            comboSubject.setToolTipText(elementDefinition[comboSubject.getSelectedIndex()].getDescription());
387            for (int i = 0; i < comboDimension.length; i++) {
388                comboDimension[i].removeAllItems();
389                for (int j = 0; j < dataFilter[comboSubject.getSelectedIndex()][i].length; j++) {
390                    comboDimension[i].addItem(dataFilter[comboSubject.getSelectedIndex()][i][j].getName());
391                }
392            }
393            updateComboDimension();
394    
395        }
396    
397        /**
398         * Updates the dimension combo-boxes.
399         */
400        protected void updateComboDimension() {
401            for (int i = 0; i < comboDimension.length; i++) {
402                if (comboDimension[i].getSelectedIndex() != -1) {
403                    comboDimension[i].setToolTipText(dataFilter[comboSubject.getSelectedIndex()][i][comboDimension[i].getSelectedIndex()].getDescription());
404                }
405            }
406        }
407    
408        /**
409         * Button "Apply" is clicked.
410         */
411        protected void dialogApply() {
412            dialog.setVisible(false);
413            subjectDefinition = elementDefinition[comboSubject.getSelectedIndex()];
414            name = textName.getText();
415            if (name.indexOf(subjectDefinition.getName()) == -1)
416                window.setTitle(name + " - " + subjectDefinition.getName());
417            else
418                window.setTitle(name);
419            reset();
420            updateConfiguration();
421        }
422    
423        /**
424         * Button "Cancel" is clicked.
425         */
426        protected void dialogCancel() {
427            dialog.setVisible(false);
428        }
429    
430        public void updateMenu() {
431            for (int i = 0; i < referenceDimension.length; i++) {
432                ArrayList comparatorList = referenceDimension[i].getComparator();
433                itemSort[i] = new JMenuItem[comparatorList.size()];
434                menuDimension[i].removeAll();
435                for (int j = 0; j < itemSort[i].length; j++) {
436                    itemSort[i][j] = new JMenuItem(((EntityComparator)(comparatorList.get(j))).getName());
437                    itemSort[i][j].addActionListener(new ActionListener() {
438                        public void actionPerformed(ActionEvent e) {
439                            selectComparator(e);
440                        }
441                    });
442                    menuDimension[i].add(itemSort[i][j]);
443                }
444            }
445    
446            ArrayList menuOptional = createOptionalMenu();
447            if (menuOptional == null ) return;
448            for (int i=0; i<menuOptional.size(); i++)
449                popup.add((JMenuItem)menuOptional.get(i));
450        }
451    
452        protected ArrayList createOptionalMenu() {
453            return null;
454        }
455    
456        public void selectComparator(ActionEvent e) {
457            for (int i = 0; i < itemSort.length; i++) {
458                for (int j = 0; j < itemSort[i].length; j++) {
459                    if (itemSort[i][j] == e.getSource()) {
460                        referenceDimension[i].selectComparator(j);
461                        sort();
462                        return;
463                    }
464                }
465            }
466        }
467    
468        protected void addPopupTrigger(Component component) {
469            Component target = component;
470            if (target instanceof JScrollPane)
471                target = ((JScrollPane)target).getViewport().getView();
472    
473            target.addMouseListener(new MouseAdapter() {
474                public void mouseReleased(MouseEvent e) {
475                    if (e.isPopupTrigger()) {
476                        showPopup(e);
477                    }
478                }
479    
480                public void mousePressed(MouseEvent e) {
481                    if (e.isPopupTrigger()) {
482                        showPopup(e);
483                    }
484                }
485            });
486        }
487    
488        public void enableSortMenu() {
489            menuSort.setEnabled(true);
490        }
491    
492        /**
493         * Creates the dimensions.
494         *
495         * @return  the dimensions
496         */
497        public abstract Dimension[] createDimension();
498    
499        /**
500         * Creates available selection menu items.
501         *
502         * @return  the array of menu items
503         */
504        public abstract JMenuItem[] createSelectionMenuItem();
505    
506        /**
507         * Creates the panel.
508         *
509         * @return  the panel
510         */
511        protected abstract Component createPanel();
512    
513        /**
514         * Updates the configuration.
515         */
516        protected abstract void updateConfiguration();
517    
518        /**
519         * Gets ready for receiving data.
520         */
521        public abstract void preVisualize();
522    
523        /**
524         * Receives data.
525         *
526         * @param  data  the data
527         */
528        protected abstract void receiveElement(Element element);
529    
530        /**
531         * Visualizes after receiving data.
532         */
533        public abstract void visualize();
534    
535        /**
536         * sorting the visualization with selected comparator
537         */
538        public abstract void sort();
539    
540        /**
541         * according to user selectiong, get a data subset
542         */
543        public abstract void makeSelection();
544    
545        /**
546         *
547         * @return a Hash map contains all current visualization's configure
548         */
549        public HashMap getCurrentConfigure() {
550            HashMap configure = new HashMap();
551    
552            configure.put("Factory",getFactory());
553            configure.put("Subject",subjectDefinition);
554            configure.put("Name",name);
555            configure.put("Interval", new Integer(-1));
556            configure.put("Options", ";not using any options");
557    
558            return configure;
559        }
560    
561        /**
562         * following are all interfaces needed for predefined viz
563         */
564        public void autoUpdateConfiguration(HashMap config) {
565    
566            int selectedSubjectId = -1;
567            Dimension [] predefinedDimension;
568    
569            for (int i=0; i< elementDefinition.length; i++) {
570                if (elementDefinition[i].getName().equals(((ElementDefinition)config.get("Subject")).getName())) {
571                    subjectDefinition = elementDefinition[i];
572                    selectedSubjectId = i;
573                    break;
574                }
575            }
576    
577            predefinedDimension = (Dimension[])config.get("Dimension");
578            for (int i=0; i<dimension.length; i++) {
579                for (int j=0; j< dataFilter[selectedSubjectId][i].length;j++) {
580                    if (dataFilter[selectedSubjectId][i][j].getName().equals(predefinedDimension[i].getDataFilter().getName())) {
581                        dimension[i].setDataFilter(dataFilter[selectedSubjectId][i][j]);
582                        break;
583                    }
584                }
585            }
586    
587            updateConfiguration();
588            window.setTitle(name);
589            menuSort.setEnabled(false);
590        }
591    
592        public EVolve.Window getWindow() {
593            return window;
594        }
595    
596        public void setFactory(VisualizationFactory factory) {
597            this.factory = factory;
598        }
599    
600        public VisualizationFactory getFactory() {
601            return factory;
602        }
603    
604        public void autoSave(String path, String dataFn) {
605            Component target = panel;
606            if (target instanceof JScrollPane)
607                target = ((JScrollPane)panel).getViewport().getView();
608    
609            BufferedImage image = new BufferedImage(target.getWidth(), target.getHeight(), BufferedImage.TYPE_INT_RGB);
610            target.paint(image.getGraphics());
611            ImageWriter writer = (ImageWriter)(ImageIO.getImageWritersByFormatName("png").next());
612    
613            try {
614                RandomAccessFile file = new RandomAccessFile((path+File.separator+
615                        dataFn.substring(dataFn.lastIndexOf(File.separator)+1,dataFn.length()-4)+".png"),"rw");
616                writer.setOutput(ImageIO.createImageOutputStream(file));
617                writer.write(image);
618            } catch (IOException e) {
619                System.out.println("writing file error.");
620            }
621    
622        }
623    
624        /**
625         * following methods are used in unify/overlap viz
626         */
627        public abstract ReferenceDimension getLinkableDimension(int dim);
628    
629        public int getVisualizationID() {
630            return linkageId;
631        }
632    
633        public Dimension[] getDimension() {
634            return dimension;
635        }
636    
637        public VisualizationDefinition getDefinition() {
638            return definition;
639        }
640    
641        public long getxMax() {
642            return -1;
643        }
644    
645        public AutoImage getImage() {
646            return null;
647        }
648    
649        public void setImage(AutoImage image) {
650            return;
651        }
652    
653        public void cleanup() {
654            dimension = null;
655            comboDimension = null;
656            menuDimension = null;
657            itemSort = null;
658            referenceDimension = null;
659            elementDefinition = null;
660            dataFilter = null;
661        }
662    
663        protected int switchOption(boolean turn_on, int option, int add_on_option) {
664            int returnVal;
665            if (turn_on)
666                returnVal = option | add_on_option;
667            else
668                returnVal = option & ~add_on_option;
669            return returnVal;
670        }
671    
672        public void setDataSourceId(int dataSourceId) {
673            this.dataSourceId = dataSourceId;
674        }
675    
676        public int getDataSourceId() {
677            return dataSourceId;
678        }
679    
680        protected void reset() {
681            for (int i = 0; i < dimension.length; i++) {
682                int selected = comboDimension[i].getSelectedIndex();
683                if (selected < dataFilter[comboSubject.getSelectedIndex()][i].length)
684                    dimension[i].setDataFilter(dataFilter[comboSubject.getSelectedIndex()][i][selected]);
685                if (dimension[i] instanceof ReferenceDimension) ((ReferenceDimension)dimension[i]).clearEntityMap();
686            }
687            menuSort.setEnabled(false);
688        }
689    
690        public boolean isFreezed() {
691            return freezed;
692        }
693    
694        public Object clone() {
695           Visualization o = null;
696    
697           try {
698               o = (Visualization)super.clone();
699           } catch (CloneNotSupportedException e) {
700               e.printStackTrace();
701               return null;
702           }
703    
704           o.name = name + "- cloned";
705           o.definition = (VisualizationDefinition)definition.clone();
706           o.subjectDefinition = (ElementDefinition) subjectDefinition.clone();
707           o.dimension = new Dimension[dimension.length];
708           o.factory = (VisualizationFactory)factory.clone();
709           o.elementDefinition = new ElementDefinition[elementDefinition.length];
710           for (int i=0; i<elementDefinition.length; i++)
711               o.elementDefinition[i] = (ElementDefinition)elementDefinition[i].clone();
712           o.dataFilter = new DataFilter[dataFilter.length][][];
713           o.linkageId = vizIDPool++;
714           for (int i=0; i<dataFilter.length; i++) {
715               o.dataFilter[i] = new DataFilter[dataFilter[i].length][];
716               for (int j=0; j<dataFilter[i].length; j++) {
717                   o.dataFilter[i][j] = new DataFilter[dataFilter[i][j].length];
718                   for (int k=0; k<dataFilter[i][j].length; k++)
719                       o.dataFilter[i][j][k] = (DataFilter)dataFilter[i][j][k].clone();
720               }
721           }
722           return o;
723       }
724    }